package zouran.service.impl;

import org.springframework.stereotype.Service;
import zouran.entity.Distance;
import zouran.entity.EarthquakeInfo;
import zouran.entity.Hospital;
import zouran.entity.IntensityLine;
import zouran.mapper.HospitalMapper;
import zouran.service.EarthquakeInfoService;
import zouran.service.HospitalService;
import zouran.service.IntensityLineService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Author：zouran
 * Date：2023/3/31  20:58
 * Description:
 */
@Service
public class HospitalServiceImpl implements HospitalService {
    private static final double EARTH_RADIUS = 6378.137;
    private final HospitalMapper hospitalMapper;
    private final EarthquakeInfoService earthquakeInfoService;
    private final IntensityLineService intensityLineService;

    public HospitalServiceImpl(HospitalMapper hospitalMapper, EarthquakeInfoService earthquakeInfoService, IntensityLineService intensityLineService) {
        this.hospitalMapper = hospitalMapper;
        this.earthquakeInfoService = earthquakeInfoService;
        this.intensityLineService = intensityLineService;
    }

    public static double getDistance(double longitude1, double latitude1, double longitude2, double latitude2) {
        // 纬度
        double lat1 = Math.toRadians(latitude1);
        double lat2 = Math.toRadians(latitude2);
        // 经度
        double lng1 = Math.toRadians(longitude1);
        double lng2 = Math.toRadians(longitude2);
        // 纬度之差
        double a = lat1 - lat2;
        // 经度之差
        double b = lng1 - lng2;
        // 计算两点距离的公式
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(b / 2), 2)));
        // 弧长乘地球半径, 返回单位: 千米
        s = s * EARTH_RADIUS;
        return s;
    }

    public static double getDistance2(double longitude1, double latitude1, double longitude2, double latitude2) {
        //Haversine公式的最终实现方式可以有多种，比如借助转角度的函数atan2：
        double latDistance = Math.toRadians(longitude1 - longitude2);
        double lngDistance = Math.toRadians(latitude1 - latitude2);

        double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
                + Math.cos(Math.toRadians(longitude1)) * Math.cos(Math.toRadians(longitude2))
                * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return c * EARTH_RADIUS;
    }

    @Override
    public List<Hospital> getAllHospital() {
        return hospitalMapper.selectList(null);
    }

    @Override
    public Hospital getHospitalByHospitalId(long id) {
        return hospitalMapper.selectById(id);
    }

    @Override
    public HashMap<String, Object> getHospitalIdRange() {
        return hospitalMapper.getHospitalIdRange();
    }

    @Override
    public List<Hospital> getAllNearHospitalByPosition(Double lon, Double lat, Double lonDistance, Double latDistance) {
        List<Hospital> hospitalList = new ArrayList<>();
        int i = 1;
        //防止找不到医院，依次扩大范围
        while (hospitalList.size() == 0) {
            hospitalList = hospitalMapper.getAllNearHospital(lon, lat, lonDistance * i, latDistance * i);
            i++;
        }
        return hospitalList;
    }

    @Override
    public List<HashMap<String, Object>> getAllNearHospitalPositionByPosition(Double lon, Double lat, Double lonDistance, Double latDistance) {
        List<HashMap<String, Object>> hashMapList = new ArrayList<>();
        int i = 1;
        //防止找不到医院，依次扩大范围
        while (hashMapList.size() == 0) {
            hashMapList = hospitalMapper.getAllNearHospitalPositionByPosition(lon, lat, lonDistance * i, latDistance * i);
            i++;
        }
        return hashMapList;
    }

    @Override
    public List<Hospital> getAllNearHospitalByEarthquakeId(long id) {
        EarthquakeInfo earthquakeInfo = earthquakeInfoService.getEarthquakeInfoById(id);
        List<IntensityLine> list = intensityLineService.getIntensityLineByEarthquakeId(id);
        if (list.size() == 0)
            return getAllNearHospitalByPosition(earthquakeInfo.getLongitude(), earthquakeInfo.getLatitude(), 0.1, 0.1);
        else {
            Double lonMax = 0.0, latMax = 0.0;
            for (IntensityLine intensityLine : list) {
                if (intensityLine.getLongRadius() > lonMax) lonMax = intensityLine.getLongRadius() / 111;
                if (intensityLine.getShortRadius() > latMax) latMax = intensityLine.getShortRadius() / 111;
            }
            return getAllNearHospitalByPosition(earthquakeInfo.getLongitude(), earthquakeInfo.getLatitude(), lonMax, latMax);
        }

    }

    @Override
    public List<HashMap<String, Object>> getAllNearHospitalPositionByEarthquakeId(long id) {
        EarthquakeInfo earthquakeInfo = earthquakeInfoService.getEarthquakeInfoById(id);
        List<IntensityLine> list = intensityLineService.getIntensityLineByEarthquakeId(id);
        if (list.size() == 0)
            return getAllNearHospitalPositionByPosition(earthquakeInfo.getLongitude(), earthquakeInfo.getLatitude(), 0.1, 0.1);
        else {
            Double lonMax = 0.0, latMax = 0.0;
            for (IntensityLine intensityLine : list) {
                if (intensityLine.getLongRadius() > lonMax) lonMax = intensityLine.getLongRadius() / 111;
                if (intensityLine.getShortRadius() > latMax) latMax = intensityLine.getShortRadius() / 111;
            }
            return getAllNearHospitalPositionByPosition(earthquakeInfo.getLongitude(), earthquakeInfo.getLatitude(), lonMax, latMax);
        }
    }

    @Override
    public Distance getNearestHospitalByPosition(double lng, double lat) {
        //输入经纬度的值，遍历所有Hospital的经纬度并比较其距离
        //return getDistance(121.446014,31.215937,121.446028464238,31.2158502442799 );
        List<Hospital> hospitals = getAllNearHospitalByPosition(lng, lat, 0.1, 0.1);
        Double minDistance = Double.MAX_VALUE;
        Distance distance = new Distance();
        for (Hospital hospital : hospitals) {
            Double distanceTwoPlaces = getDistance(hospital.getLon(), hospital.getLat(), lng, lat);
            minDistance = Math.min(minDistance, distanceTwoPlaces);
            if (minDistance.equals(distanceTwoPlaces)) {
                distance.setDistance(minDistance);
                distance.setEndLon(hospital.getLon());
                distance.setEndLat(hospital.getLat());
                distance.setEndName(hospital.getName());
                distance.setEndAddress(hospital.getAddress());
            }
        }
        distance.setStartLon(lng);
        distance.setStartLat(lat);
        return distance;
    }
}
